package my.rpc.client;

import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import my.rpc.common.bean.RpcRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Map;

/**
 * RPC 代理（用于创建 RPC 服务代理）
 * @author fengbo
 * @date 2017/11/2
 */
public class RpcClient {

    private static final Logger log = LoggerFactory.getLogger(RpcClient.class);

    private final EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
    private final Map<String, RpcClientHandler> clientHandlerMap = new HashMap<>(32);

    /**
     * 单例设计模式
     */
    private static class Single {
        private static final RpcClient INSTANCE = new RpcClient();
    }
    public static RpcClient getInstance() {
        return Single.INSTANCE;
    }

    @SuppressWarnings("unchecked")
    public <T> T create(Class<?> interfaceClass, String url) {
        // 创建动态代理对象
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[]{interfaceClass},
                (proxy, method, args) -> {
                    // 创建 RPC 请求对象并设置请求属性
                    RpcRequest request = new RpcRequest();
                    request.setInterfaceName(method.getDeclaringClass().getName());
                    request.setMethodName(method.getName());
                    request.setParameterTypes(method.getParameterTypes());
                    request.setParameters(args);

                    // 创建 RPC 客户端对象并发送 RPC 请求
                    RpcClientHandler clientHandler = clientHandlerMap.get(url);
                    return clientHandler.sendRequest(request).getResult();
                }
        );
    }

    public void load(String serverAddress) {
        if (clientHandlerMap.containsKey(serverAddress)) {
            return;
        }

        if (StringUtils.isEmpty(serverAddress)) {
            log.error("server address is empty!");
            throw new RuntimeException("server address is empty");
        }

        String[] ipAddress = StringUtils.split(serverAddress, ":");
        String host = ipAddress[0];
        int port = Integer.parseInt(ipAddress[1]);
        SocketAddress address = new InetSocketAddress(host, port);
        RpcClientHandler clientHandler = new RpcClientTask(eventLoopGroup).connect(address);
        clientHandlerMap.put(serverAddress, clientHandler);
    }

    public void unLoad(String url) {
        RpcClientHandler clientHandler = clientHandlerMap.remove(url);
        if (clientHandler != null) {
            clientHandler.close();
        }
        if (clientHandlerMap.isEmpty()) {
            eventLoopGroup.shutdownGracefully();
        }
    }
}
